package com.budwk.app.miniapp.controllers.admin;

import cn.dev33.satoken.annotation.SaCheckLogin;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import com.budwk.app.miniapp.models.*;
import com.budwk.app.miniapp.models.vo.WxEventStatisticVO;
import com.budwk.app.miniapp.services.SysdictdataService;
import com.budwk.app.miniapp.services.WxUserService;
import com.budwk.app.miniapp.services.WxeventscheduleService;
import com.budwk.app.miniapp.models.vo.EventScheduleDetailVO;
import com.budwk.app.miniapp.utils.DistanceUtils;
import com.budwk.app.miniapp.utils.ExcelUtils;
import com.budwk.app.wxprogram.models.Wx_event_schedule;
import com.budwk.starter.common.constant.RedisConstant;
import com.budwk.starter.common.result.Result;
import com.budwk.starter.common.result.ResultCode;
import com.budwk.starter.common.page.Pagination;
import com.budwk.starter.log.annotation.SLog;
import com.budwk.starter.security.utils.SecurityUtil;
import com.budwk.app.miniapp.services.WxeventService;
import org.apache.commons.lang3.StringUtils;
import org.nutz.dao.Cnd;
import org.nutz.integration.jedis.RedisService;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.json.Json;
import org.nutz.lang.random.R;
import org.nutz.lang.util.NutMap;
import org.nutz.mvc.annotation.*;
import com.budwk.starter.common.openapi.annotation.*;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.constraints.NotEmpty;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@IocBean
@At("/platform/miniapp/event")
@ApiDefinition(tag = "微信小程序-事件管理(求助/求救)")
@Slf4j
public class WxeventController {
    @Inject
    private WxeventService wxEventService;

    @Inject
    private SysdictdataService sysdictdataService;

    @Inject
    private WxeventscheduleService wxeventscheduleService;

    @Inject
    private RedissonClient redissonClient;
    @Inject
    private WxUserService wxUserService;


    @At("/list")
    @Ok("json")
    @POST
    @ApiOperation(name = "分页查询事件信息")
    @ApiFormParams(
            value = {
                    @ApiFormParam(name = "pageNo", example = "1", description = "页码", type = "integer"),
                    @ApiFormParam(name = "pageSize", example = "10", description = "页大小", type = "integer"),
                    @ApiFormParam(name = "pageOrderName", example = "createdAt", description = "排序字段"),
                    @ApiFormParam(name = "pageOrderBy", example = "descending", description = "排序方式")
            },
            implementation = WxEvent.class
    )
    @ApiResponses(
            implementation = WxEvent.class
    )
    public Result<?> list(@Param("..") WxEvent bean,
                          @Param("pageNo") int pageNo,
                          @Param("pageSize") int pageSize,
                          @Param("pageOrderName") String pageOrderName,
                          @Param("pageOrderBy") String pageOrderBy) {


        Pagination pagination = wxEventService.listPage(pageNo,pageSize, wxEventService.querySqlBuild(bean, pageOrderName, pageOrderBy));
        List<WxEvent> myEventList= (List<WxEvent>) pagination.getList();

        try {

            if(CollectionUtil.isNotEmpty(myEventList)) {
                if (ObjectUtil.isNotEmpty(bean.getLoginOpenid())&ObjectUtil.isNotEmpty(bean.getLatitude())&ObjectUtil.isNotEmpty(bean.getLongitude())){
                    myEventList.forEach(s -> {
                            //计算事件离我的距离
                            Double distance = DistanceUtils.latitudeLongitudeDistance(s.getLatitude(), s.getLongitude(),
                                    Double.valueOf(bean.getLatitude()), Double.valueOf(bean.getLongitude()));
                            String distanceMe="";
                            if(distance>100&&distance<1000){
                                distanceMe="距离"+ NumberUtil.roundStr(distance,0)+"m";
                            }else if(distance>1000) {
                                distanceMe="距离"+NumberUtil.roundStr(distance/1000,1)+"km";
                            }else if(distance<100){
                                distanceMe="距离少于100m";
                            }

                            s.setDistanceForMe(distance);
                            s.setDistanceMe(distanceMe);
                    });
                    myEventList= myEventList.stream().sorted(Comparator.comparing(WxEvent::getDistanceForMe)).collect(Collectors.toList());
                }
                //我发布的置顶
                pagination.setList(myEventList);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return Result.data(pagination);
    }

    /**
     * 获取事件在总条数
     */
    @At("/getCountInfo")
    @Ok("json")
    @POST
    @ApiOperation(name = "新增事件信息")
    @ApiResponses
    @SLog(tag = "新增事件信息", value = "${args[0].id}")
    @SaCheckLogin
    public Result<?> getCountInfo() {
        HashMap map = new HashMap<>();
        WxEvent bo = new WxEvent();
        Integer total = wxEventService.count();
        bo.setCompleteType(true);
        Cnd cnd = Cnd.NEW();
        cnd.and("complete_type", "=", "true");
        Integer complete = wxEventService.count(cnd);
        map.put("total", total);
        map.put("complete", complete);
        return Result.success(map);
    }

    /**
     * 导出事件列表
     */
    @At("/export")
    @Ok("json")
    @POST
    @ApiOperation(name = "导出事件信息")
    @ApiFormParams(
            implementation = WxEvent.class
    )
    @ApiResponses
    @SLog(tag = "导出事件信息", value = "${args[0].id}")
    @SaCheckLogin
    public void export(WxEvent bo, HttpServletResponse response) {
        List<WxEvent> list = wxEventService.query(wxEventService.querySqlBuild(bo, null, null));
        ExcelUtils.exportExcel(list, "事件", WxEvent.class, response);
    }

    /**
     * 新增事件
     */
    @At("/add")
    @Ok("json")
    @POST
    @ApiOperation(name = "新增事件信息")
    @ApiFormParams(
            implementation = WxEvent.class
    )
    @ApiResponses
    @SLog(tag = "新增事件信息", value = "${args[0].id}")
    @SaCheckLogin
    public Result<?> create(@Param("..") WxEvent wxEvent, HttpServletRequest req) {
        WxUser user=wxUserService.fetch(SecurityUtil.getUserId());
        wxEvent.setOpenid(user.getOpenId());
        wxEvent.setUserName(user.getNickName());
        wxEvent.setId(String.valueOf(UUID.randomUUID()));
        if(ObjectUtil.isNotEmpty(user.getNickName())){
            wxEvent.setUserName(user.getNickName());
        }else {
            wxEvent.setUserName("微信用户");
        }
        wxEvent.setCreateTime(new Date());
        wxEvent.setUpdateTime(new Date());
        wxEvent.setEventCreateTime(new Date());
        wxEvent.setCreateBy(SecurityUtil.getUserId());
        wxEvent.setEventStatus("N");
        // 事件推送
        // iWxEventPushService.eventPushTakeNotes(bo);
        WxEvent entity = wxEventService.insert(wxEvent);
        wxEventService.clearSetCacheList();

        // wzy 2023-4-18 新增的事件可能需要推送一些志愿者团队信息
        // wxEventScheduleService.insertWxEventScheduleService(bo);

        return Result.success(entity);
    }

    /**
     * 修改事件
     */
    @At("/update")
    @Ok("json")
    @POST
    @ApiOperation(name = "修改事件信息")
    @ApiFormParams(
            implementation = WxEvent.class
    )
    @ApiResponses
    @SLog(tag = "修改事件信息", value = "${args[0].id}")
    @SaCheckLogin
    public Result<?> update(@Param("..") WxEvent wxEvent, HttpServletRequest req) {
        wxEvent.setUpdateBy(SecurityUtil.getUserId());
        wxEvent.setUpdateTime(new Date());
        // 使用redisson锁的原因是避免并发同时抢单
        RLock lock = redissonClient.getLock("redissonLock-eventEdit:" + wxEvent.getId());
        try {
            boolean lockFlag = lock.tryLock(1, TimeUnit.SECONDS);
            if (lockFlag) {
                // wzy 2023-4-21 将业务代码放在了redisson锁内   其实这种写法相当不专业，因为这个接口实在是太多人用了，会卡的
                // 业务代码开始
                // 如果是志愿者接单，那么就需要判断一下是否需要进行相应的业务
                if ("pending_evaluation".equals(wxEvent.getEventStatus())) {
                    //查询一次数据库看一下状态是否已经被改变了
                    WxEvent queryWxEvent = wxEventService.fetch(wxEvent.getId());
                    wxEvent.setEventStreet(queryWxEvent.getEventStreet());
                    if (queryWxEvent == null) {
                        return Result.error("该条请求不存在");
                    }
                    if ("pending_evaluation".equals(queryWxEvent.getEventStatus())) {
                        return Result.error("已经有其他志愿者回应了该次请求");
                    }
                }
                try {
                    if (wxEvent.getEventStatus() != null) {
                        wxeventscheduleService.insertWxEventScheduleService(wxEvent);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }finally {
                    lock.unlock();
                }
                wxEventService.clearSetCacheList();
                if (wxEventService.updateByBo(wxEvent)) {
                    Cnd cnd = Cnd.NEW();
                    cnd.and("id", "=", wxEvent.getId());
                    WxEvent queryWxEvent = wxEventService.fetch(cnd);
                    return Result.success(queryWxEvent);
                }
                // 业务代码结束
            }
        } catch (Exception e) {
            return Result.error();
        } finally {
            //判断要解锁的key是否被当前线程持有。
            if (lock.isLocked() && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
        return Result.error("修改事件异常");
    }


    @At
    @Ok("json")
    @POST
    @ApiOperation(name = "修改事件信息")
    @ApiFormParams(
            implementation = WxEvent.class
    )
    @ApiResponses
    @SLog(tag = "修改事件信息", value = "${args[0].id}")
    @SaCheckLogin
    public Result<?> updateNew(@Param("..") WxEvent wxEvent) {
        wxEvent.setEventStatus("finished");
        wxEventService.updateIgnoreNull(wxEvent);
        WxEventSchedule wxEventScheduleBo = new WxEventSchedule();
        wxEventScheduleBo.setId(R.UU32());
        wxEventScheduleBo.setEventId(wxEvent.getId());
        wxEventScheduleBo.setEventTime(DateUtil.format(new Date(),"yyyy-MM-dd HH:mm:ss"));
        wxEventScheduleBo.setEventType(wxEvent.getEventType());
        wxEventScheduleBo.setEventStatus(wxEvent.getEventStatus());
        wxeventscheduleService.insert(wxEventScheduleBo);
        return Result.success();
    }

    /**
     * 获取事件详情
     */
    @At("/detail")
    @Ok("json")
    @POST
    @ApiOperation(name = "获取事件详情")
    @ApiFormParams(
            {
                    @ApiFormParam(name = "id", description = "唯一标识", required = true, check = true)
            }
    )
    @SaCheckLogin
    public Result<?> getData(@Param("id") String id, HttpServletRequest req) {
       WxEvent wxEvent = wxEventService.fetch(id);
        if (wxEvent == null) {
            return Result.error(ResultCode.NULL_DATA_ERROR);
        }
        return Result.data(wxEvent);
    }

    /**
     * 删除事件
     *
     * @param ids 主键串
     */
    @At("/delete")
    @Ok("json")
    @DELETE
    @ApiOperation(name = "删除事件信息")
    @ApiFormParams(
            {
                    @ApiFormParam(name = "id", description = "唯一标识", required = true, check = true)
            }
    )
    @SLog(tag = "删除事件信息", value = "${args[0]}")
    @SaCheckLogin
    public Result<?> remove(@NotEmpty(message = "主键不能为空") Long[] ids) {
        wxEventService.delete(ids);
        wxEventService.clearSetCacheList();
        return Result.success(true);
    }


    @At("/preAdd")
    @Ok("json")
    @POST
    @ApiOperation(name = "审核失败重新发布事件(二次发布)")
    @ApiFormParams(
            implementation = WxEvent.class
    )
    @ApiResponses
    @SLog(tag = "审核失败重新发布事件(二次发布)", value = "${args[0].id}")
    @SaCheckLogin
    //@SaCheckPermission("miniapp.event.create")
    public Result<?> preAdd(@Param("..") WxEvent wxEvent, HttpServletRequest req) {
        if (StringUtils.isBlank(wxEvent.getId())) {
            return Result.error("Id不能为空");
        }
        WxEvent wxEventR = wxEventService.preAdd(wxEvent);
        Wx_event_schedule wx_event_schedule = new Wx_event_schedule();
        wx_event_schedule.setEvent_id(wxEvent.getId());
        wx_event_schedule.setEvent_type(wxEvent.getEventType());
        wx_event_schedule.setEvent_status(wxEvent.getEventStatus());
        wx_event_schedule.setEvent_content("您的求助信息正在人工审核中，请耐心等待");
        wx_event_schedule.setCreatedBy(SecurityUtil.getUserUsername());
        SimpleDateFormat simpleDateFormat=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        wx_event_schedule.setEvent_time(simpleDateFormat.format(new Date()));
        wxeventscheduleService.insert(wx_event_schedule);
        return Result.success(wxEventR);
    }

    @At("/dictListWithSub")
    @Ok("json")
    @POST
    @ApiOperation(name = "获取帮助类型")
    @ApiFormParams(
            implementation = SysDictData.class
    )
    @ApiResponses
    @SaCheckLogin
    //@SLog(tag = "获取帮助类型", value = "${args[0].dictCode}")
    public Result<List<DictDataWithSubLists>> dictListWithSub(@Param("..") SysDictData sysDictData) {
        List<DictDataWithSubLists> resultList = sysdictdataService.dictListWithSub(sysDictData);
        return Result.success(resultList);
    }

    /**
     * 2023-4-18 wzy
     * 根据事件的状态 求助类别 求助类型 经纬度信息查询附近的志愿者站点信息
     * 供小程序使用
     *
     * @param wxEventBo 事件bo
     * @return 结果
     */
    @At("/listNearestVolunteerTeam")
    @Ok("json")
    @POST
    @ApiOperation(name = "根据事件的状态 求助类别 求助类型 经纬度信息查询附近的志愿者站点信息")
    @ApiFormParams(
            implementation = WxEvent.class
    )
    @ApiResponses(
            implementation = EventScheduleDetailVO.class
    )
    @SaCheckLogin
    public Result<?> listNearestVolunteerTeam(@Param("..") WxEvent wxEventBo) {
        List<EventScheduleDetailVO> resultList = wxeventscheduleService.createEventContentByEventType(wxEventBo);
        return Result.success(resultList);
    }

    /**
     * 事件推送
     *
     * @param
     */
    @At("/eventSendMessage")
    @Ok("json")
    @POST
    @ApiOperation(name = "事件推送")
    @ApiFormParams(
            {
                    @ApiFormParam(name = "openId", description = "身份ID", required = true, check = true),
                    @ApiFormParam(name = "eventId", description = "事件ID", required = true, check = true)
            }
    )
    public Result<Void> eventSendMessage(@Param("openId") String openId,@Param("eventId") String eventId) {
        try {
            wxEventService.sendMessage(openId, eventId);
        }catch (Exception e){
            return Result.error(e.getMessage());
        }

        return Result.success();
    }

    /**
     * 2023-4-18 wzy
     * 小程序首页统计数据
     *
     * @return 结果
     */
    @At("/getHomePageEventStatistic")
    @Ok("json")
    @GET
    @ApiOperation(name = "小程序首页统计数据")
    public Result<WxEventStatisticVO> getHomePageEventStatistic() {
        WxEventStatisticVO resultVO = wxEventService.getHomePageEventStatistic();
        return Result.success(resultVO);
    }

    /**
     * wzy 2023-5-8 重写查询首页事件接口
     */
    @At("/homePageList")
    @Ok("json")
    @POST
    @ApiOperation(name = "查询首页求助/求救列表")
    @ApiFormParams(
            value = {
                    @ApiFormParam(name = "pageNo", example = "1", description = "页码", type = "integer"),
                    @ApiFormParam(name = "pageSize", example = "10", description = "页大小", type = "integer"),
                    @ApiFormParam(name = "pageOrderName", example = "createdAt", description = "排序字段"),
                    @ApiFormParam(name = "pageOrderBy", example = "descending", description = "排序方式")
            },
            implementation = WxEvent.class
    )
    @ApiResponses(
            implementation = WxEvent.class
    )
    @SaCheckLogin
    public Result<?> homePageList(@Param("..") WxEvent bean,
                                  @Param("pageNo") int pageNo,
                                  @Param("pageSize") int pageSize) {
        Pagination pagination = wxEventService.homePageList(bean, pageNo, pageSize);
        return Result.success(pagination);
    }

}
